/*
 * @Author: Troubadour 2276791354@qq.com
 * @Date: 2023-04-05 14:54:05
 * @LastEditors: Troubadour 2276791354@qq.com
 * @LastEditTime: 2024-03-08 19:48:33
 * @FilePath: \MDK-ARMf:\Study\STM32_Project\STM32F4_Project\OLED_Display\HARDWARE\OLED\oled.c
 * @Description:
 */
#include "oled.h"
#include "stdlib.h"
#include "string.h"

/* ------------------------------------------> 2. Modify Macro Definition <------------------------------------------ */
/* OLED尺寸选择 */
#define OLED_0_96   1
#define OLED_1_3    0
#define OLED_SH1106 0
/* ------------------------------------------> 3. Oled addr  <------------------------------------------ */
#define IIC_SLAVE_ADDR 0x78
/* ------------------------------------------> 4. Select driving mode  <------------------------------------------ */
#define OLED_COM_HARDWARE   1       /* oled communication hardware mode */
#define OLED_COM_SOFTWARE   0       /* oled communication software mode */

#if (OLED_COM_HARDWARE)
#define OLED_COM_HARDWARE_IIC 1
#define OLED_COM_HARDWARE_IIC_DMA 0
#elif (OLED_COM_SOFTWARE)
#define OLED_COM_SOFTWARE_IIC 1
#endif

//-------------写命令和数据定义-------------------
#define OLED_CMD     0	//写命令
#define OLED_DATA    1	//写数据
//--------------OLED参数定义---------------------
#define XLevelH		 0x10
#define YLevel       0xB0
#define	Brightness	 0xDF
#define PAGE_SIZE    8
#if  (OLED_1_3 || OLED_SH1106)
#define XLevelL		 0x02
#else
#define XLevelL		 0x00
#endif

// OLED显存总共分为4页
// 每页8行，一行128个像素点
// OLED的显存
// 存放格式如下.
//[0]0 1 2 3 ... 127 (0~7)行
//[1]0 1 2 3 ... 127 (8~15)行
//[2]0 1 2 3 ... 127 (16~23)行
//[3]0 1 2 3 ... 127 (24~31)行
// 数组每个bit存储OLED每个像素点的颜色值(1-亮(白色),0-灭(黑色))
// 每个数组元素表示1列8个像素点，一共128列.0
static unsigned char OLED_buffer[1024] = {0};


/* ------------------------------------------> 5. Modify your IIC  <------------------------------------------ */
/* ------------------------------------------> 5.1. Modify function of the initialization  <------------------------------------------ */
/* ------------------------------------------> 5.2. Modify function of the wtier data  <------------------------------------------ */
/* ------------------------------------------> 5.3. Modify function of the OLED_Display  <------------------------------------------ */
#if (OLED_COM_HARDWARE)
#include "i2c.h"
#include "stm32f1xx_hal_dma.h"

/**
 * @function: OLED_IIC_Init
 * @brief:
 * @return {*}
 */
void OLED_IIC_Init(void)
{

}

/**
 * @function: OLED_Write_Byte
 * @brief:
 * @param {uint8_t} IIC_Data: Write data.
 * @param {uint8_t} class: Select OLED_CMD or OLED_DATA.
 * @return {*}
 */
void OLED_Write_Byte(uint8_t IIC_Data, uint8_t class)
{
    HAL_I2C_Mem_Write(&hi2c1, IIC_SLAVE_ADDR, (class == OLED_CMD) ? (0x00) : (0x40), 1, &IIC_Data, 1, 100);
}

#if (OLED_COM_HARDWARE_IIC)

void OLED_Display(void)
{
    uint8_t i = 0;

    /* 优化OLED显示屏刷新速度 */
    for (i = 0; i < 8; i++)
    {
        OLED_Write_Byte(YLevel + i, OLED_CMD); // 设置行起始地址
        OLED_Write_Byte(XLevelL, OLED_CMD);  // 设置低列起始地址
        OLED_Write_Byte(XLevelH, OLED_CMD);  // 设置高列起始地址
        HAL_I2C_Mem_Write(&hi2c1, IIC_SLAVE_ADDR, 0x40, 1, OLED_buffer + (i * OLED_WIDTH), 128, 1000);
    }
}
#elif (OLED_COM_HARDWARE_IIC_DMA)

uint8_t OLED_CMDbuf[8][4] = {
    0x00, YLevel + 0, XLevelL, XLevelH,
    0x00, YLevel + 1, XLevelL, XLevelH,
    0x00, YLevel + 2, XLevelL, XLevelH,
    0x00, YLevel + 3, XLevelL, XLevelH,
    0x00, YLevel + 4, XLevelL, XLevelH,
    0x00, YLevel + 5, XLevelL, XLevelH,
    0x00, YLevel + 6, XLevelL, XLevelH,
    0x00, YLevel + 7, XLevelL, XLevelH,
    };

void OLED_Display(void)
{
    for (uint8_t i = 0; i < 8; i++)
    {
        HAL_I2C_Master_Transmit_DMA(&hi2c1, IIC_SLAVE_ADDR, OLED_CMDbuf[i], 4);
        while (hi2c1.State != HAL_I2C_STATE_READY);
        HAL_I2C_Mem_Write_DMA(&hi2c1, IIC_SLAVE_ADDR, 0x40, 1, OLED_buffer + (i * OLED_WIDTH), 128);
        while (hi2c1.State != HAL_I2C_STATE_READY);
    }
}
#endif

#elif (OLED_COM_SOFTWARE && OLED_COM_SOFTWARE_IIC)

#include "SoftAnalogIIC.h"

#define OLED_SDA_PORT IIC_SDA_GPIO_Port
#define OLED_SDA_PIN IIC_SDA_Pin
#define OLED_SCL_PORT IIC_SCL_GPIO_Port
#define OLED_SCL_PIN IIC_SCL_Pin

SoftAnalogIIC_Typedef oled_iic;

/**
 * @function: OLED_IIC_Init
 * @brief:
 * @return {*}
 */
void OLED_IIC_Init(void)
{
    oled_iic.SCL_GPIOx = OLED_SCL_PORT;
    oled_iic.SCL_GPIO_Pin = OLED_SCL_PIN;
    oled_iic.SDA_GPIOx = OLED_SDA_PORT;
    oled_iic.SDA_GPIO_Pin = OLED_SDA_PIN;
    SoftAnalogIIC_Init(&oled_iic);
}

/**
 * @function: OLED_Write_Byte
 * @brief:
 * @param {uint8_t} IIC_Data: Write data.
 * @param {uint8_t} class: Select OLED_CMD or OLED_DATA.
 * @return {*}
 */
void OLED_Write_Byte(uint8_t IIC_Data, uint8_t class)
{
    SoftAnalogIIC_Start(&oled_iic);
    SoftAnalogIIC_Send_Byte(&oled_iic, IIC_SLAVE_ADDR);
    SoftAnalogIIC_Wait_Ack(&oled_iic);
    SoftAnalogIIC_Send_Byte(&oled_iic, (class == OLED_CMD) ? (0x00) : (0x40));
    SoftAnalogIIC_Wait_Ack(&oled_iic);
    SoftAnalogIIC_Send_Byte(&oled_iic, IIC_Data);
    SoftAnalogIIC_Wait_Ack(&oled_iic);
    SoftAnalogIIC_Stop(&oled_iic);
}
void OLED_Display(void)
{
    uint8_t i, n;

    /* 优化OLED显示屏刷新速度 */
    for (i = 0; i < 8; i++)
    {
        OLED_Write_Byte(YLevel + i, OLED_CMD); // 设置行起始地址
        OLED_Write_Byte(XLevelL, OLED_CMD);  // 设置低列起始地址
        OLED_Write_Byte(XLevelH, OLED_CMD);  // 设置高列起始地址
        SoftAnalogIIC_Start(&oled_iic);
        SoftAnalogIIC_Send_Byte(&oled_iic, IIC_SLAVE_ADDR);
        SoftAnalogIIC_Wait_Ack(&oled_iic);
        SoftAnalogIIC_Send_Byte(&oled_iic, 0x40);
        SoftAnalogIIC_Wait_Ack(&oled_iic);
        for (n = 0; n < 128; n++)
        {
            SoftAnalogIIC_Send_Byte(&oled_iic, OLED_buffer[i * OLED_WIDTH + n]);
            SoftAnalogIIC_Wait_Ack(&oled_iic);
        }
        SoftAnalogIIC_Stop(&oled_iic);
    }
}
#endif

/*******************************************************************
 * @name       :void OLED_Set_Pos(unsigned char x, unsigned char y)
 * @date       :2018-08-27
 * @function   :Set coordinates in the OLED screen
 * @parameters :x:x coordinates
                y:y coordinates
 * @retvalue   :None
********************************************************************/
void OLED_Set_Pos(unsigned char x, unsigned char y)
{
    OLED_Write_Byte(YLevel + y / PAGE_SIZE, OLED_CMD);
    OLED_Write_Byte(((x & 0xf0) >> 4) | 0x10, OLED_CMD);
    OLED_Write_Byte((x & 0x0f), OLED_CMD);
}

/*******************************************************************
 * @name       :void OLED_Display_On(void)
 * @date       :2018-08-27
 * @function   :Turn on OLED display
 * @parameters :None
 * @retvalue   :None
 ********************************************************************/
void OLED_Display_On(void)
{
    OLED_Write_Byte(0X8D, OLED_CMD); // SET DCDC命令
    OLED_Write_Byte(0X14, OLED_CMD); // DCDC ON
    OLED_Write_Byte(0XAF, OLED_CMD); // DISPLAY ON
}

/*******************************************************************
 * @name       :void OLED_Display_Off(void)
 * @date       :2018-08-27
 * @function   :Turn off OLED display
 * @parameters :None
 * @retvalue   :None
 ********************************************************************/
void OLED_Display_Off(void)
{
    OLED_Write_Byte(0X8D, OLED_CMD); // SET DCDC命令
    OLED_Write_Byte(0X10, OLED_CMD); // DCDC OFF
    OLED_Write_Byte(0XAE, OLED_CMD); // DISPLAY OFF
}

/*******************************************************************
 * @name       :void OLED_Set_Pixel(unsigned char x, unsigned char y,unsigned char color)
 * @date       :2018-08-27
 * @function   :set the value of pixel to RAM
 * @parameters :x:the x coordinates of pixel
                y:the y coordinates of pixel
                                color:the color value of the point
                                      1-white
                                            0-black
 * @retvalue   :None
********************************************************************/
void OLED_Set_Pixel(unsigned char x, unsigned char y, unsigned char color)
{
    if (color)
    {
        OLED_buffer[(y / PAGE_SIZE) * OLED_WIDTH + x] |= (1 << (y % PAGE_SIZE)) & 0xff;
    }
    else
    {
        OLED_buffer[(y / PAGE_SIZE) * OLED_WIDTH + x] &= ~((1 << (y % PAGE_SIZE)) & 0xff);
    }
}

/*******************************************************************
 * @name       :void OLED_Clear(unsigned dat)
 * @date       :2018-08-27
 * @function   :clear OLED screen
 * @parameters :dat:0-Display full black
                    1-Display full white
 * @retvalue   :None
********************************************************************/
void OLED_Clear(unsigned dat)
{
    if (dat)
    {
        memset(OLED_buffer, 0xff, sizeof(OLED_buffer));
    }
    else
    {
        memset(OLED_buffer, 0, sizeof(OLED_buffer));
    }
    OLED_Display();
}

/*******************************************************************
 * @name       :void OLED_Init(void)
 * @date       :2018-08-27
 * @function   :initialise OLED SSD1306 control IC
 * @parameters :None
 * @retvalue   :None
 ********************************************************************/
void OLED_Init(void)
{
    OLED_IIC_Init();

/**************初始化SSD1306*****************/
#if OLED_0_96
    //	OLED_Display_Off(); //power off
    OLED_Write_Byte(0xAE, OLED_CMD); //--display off
    OLED_Write_Byte(0x00, OLED_CMD); //---set low column address
    OLED_Write_Byte(0x10, OLED_CMD); //---set high column address
    OLED_Write_Byte(0x40, OLED_CMD); //-not offset
    OLED_Write_Byte(0xB0, OLED_CMD); // contract control
    OLED_Write_Byte(0xFF, OLED_CMD); //--128
    OLED_Write_Byte(0xA1, OLED_CMD); // set segment remap
    OLED_Write_Byte(0xA6, OLED_CMD); //--normal / reverse
    OLED_Write_Byte(0xA8, OLED_CMD); //--set multiplex ratio(1 to 64)
    OLED_Write_Byte(0x3F, OLED_CMD);
    OLED_Write_Byte(0xD3, OLED_CMD); //-set display offset
    OLED_Write_Byte(0x00, OLED_CMD);
    OLED_Write_Byte(0xD5, OLED_CMD); // set osc division
    OLED_Write_Byte(0xF0, OLED_CMD);
    OLED_Write_Byte(0xD9, OLED_CMD); // Set Pre-Charge Period
    OLED_Write_Byte(0xF1, OLED_CMD);
    OLED_Write_Byte(0xDA, OLED_CMD); // set com pin configuartion
    OLED_Write_Byte(0x12, OLED_CMD);
    OLED_Write_Byte(0xDB, OLED_CMD); // set Vcomh
    OLED_Write_Byte(0x49, OLED_CMD);
    OLED_Write_Byte(0x8D, OLED_CMD); // set charge pump enable
    OLED_Write_Byte(0x14, OLED_CMD);
    OLED_Write_Byte(0xAF, OLED_CMD); //--turn on oled panel
    // 以下自己加
    OLED_Write_Byte(0xA1, OLED_CMD); //--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
    OLED_Write_Byte(0xC8, OLED_CMD); // Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
    OLED_Clear(0);
#elif OLED_1_3
    OLED_Write_Byte(0xAE, OLED_CMD); //--turn off oled panel
    OLED_Write_Byte(0x00, OLED_CMD); //---set low column address
    OLED_Write_Byte(0x10, OLED_CMD); //---set high column address
    OLED_Write_Byte(0x40, OLED_CMD); //--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
    OLED_Write_Byte(0x81, OLED_CMD); //--set contrast control register
    OLED_Write_Byte(0xCF, OLED_CMD); // Set SEG Output Current Brightness
    OLED_Write_Byte(0xA1, OLED_CMD); //--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
    OLED_Write_Byte(0xC8, OLED_CMD); // Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
    OLED_Write_Byte(0xA6, OLED_CMD); //--set normal display
    OLED_Write_Byte(0xA8, OLED_CMD); //--set multiplex ratio(1 to 64)
    OLED_Write_Byte(0x3f, OLED_CMD); //--1/64 duty
    OLED_Write_Byte(0xD3, OLED_CMD); //-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
    OLED_Write_Byte(0x00, OLED_CMD); //-not offset
    OLED_Write_Byte(0xd5, OLED_CMD); //--set display clock divide ratio/oscillator frequency
    OLED_Write_Byte(0x80, OLED_CMD); //--set divide ratio, Set Clock as 100 Frames/Sec
    OLED_Write_Byte(0xD9, OLED_CMD); //--set pre-charge period
    OLED_Write_Byte(0xF1, OLED_CMD); // Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
    OLED_Write_Byte(0xDA, OLED_CMD); //--set com pins hardware configuration
    OLED_Write_Byte(0x12, OLED_CMD);
    OLED_Write_Byte(0xDB, OLED_CMD); //--set vcomh
    OLED_Write_Byte(0x40, OLED_CMD); // Set VCOM Deselect Level
    OLED_Write_Byte(0x20, OLED_CMD); //-Set Page Addressing Mode (0x00/0x01/0x02)
    OLED_Write_Byte(0x02, OLED_CMD); //
    OLED_Write_Byte(0x8D, OLED_CMD); //--set Charge Pump enable/disable
    OLED_Write_Byte(0x14, OLED_CMD); //--set(0x10) disable
    OLED_Write_Byte(0xA4, OLED_CMD); // Disable Entire Display On (0xa4/0xa5)
    OLED_Write_Byte(0xA6, OLED_CMD); // Disable Inverse Display On (0xa6/a7)
    OLED_Clear(0);
    OLED_Write_Byte(0xAF, OLED_CMD);
#elif OLED_SH1106
    /* SS1106 */
    OLED_Write_Byte(0xAE, OLED_CMD);//set display display ON/OFF,AFH/AEH
    OLED_Write_Byte(0x02, OLED_CMD);
    OLED_Write_Byte(0x10, OLED_CMD);
    OLED_Write_Byte(0x40, OLED_CMD);//set display start line:COM0
    OLED_Write_Byte(0xB0, OLED_CMD);
    OLED_Write_Byte(0x81, OLED_CMD);//set contrast control
    OLED_Write_Byte(0xCF, OLED_CMD);
    OLED_Write_Byte(0xA1, OLED_CMD);//entire display on: A4H:OFF/A5H:ON
    OLED_Write_Byte(0xC8, OLED_CMD); //该指令控制显示方向显示方向0xc8或者0xc0
    //OLED_Write_Byte(0x, OLED_CMDC0);
    OLED_Write_Byte(0xAF, OLED_CMD);
    OLED_Write_Byte(0xA7, OLED_CMD);//set normal/inverse display: A6H:normal/A7H:inverse
    OLED_Write_Byte(0xA8, OLED_CMD);//set multiplex ratio
    OLED_Write_Byte(0x3F, OLED_CMD);//1/64duty
    OLED_Write_Byte(0xD3, OLED_CMD);//set display offset
    OLED_Write_Byte(0x00, OLED_CMD);//
    OLED_Write_Byte(0xD5, OLED_CMD);//set display clock divide ratio/oscillator frequency
    OLED_Write_Byte(0x80, OLED_CMD);//105Hz
    OLED_Write_Byte(0xD9, OLED_CMD);//Dis-charge /Pre-charge Period Mode Set
    OLED_Write_Byte(0xF1, OLED_CMD);//
    OLED_Write_Byte(0xDA, OLED_CMD);//Common Pads Hardware Configuration Mode Set
    OLED_Write_Byte(0x12, OLED_CMD);//
    OLED_Write_Byte(0xDB, OLED_CMD);//set vcomh deselect level
    OLED_Write_Byte(0x40, OLED_CMD);//VCOM = β X VREF = (0.430 + A[7:0] X 0.006415) X VREF
    OLED_Write_Byte(0xA4, OLED_CMD);
    OLED_Write_Byte(0xA6, OLED_CMD);
    OLED_Clear(0);
    OLED_Write_Byte(0xAF, OLED_CMD);//set display display ON/OFF,AEH/AFH
	//这两条指令是设置反显，取消注释即为打开反显，
	//OLED_Write_Byte(0xAF);
	//OLED_Write_Byte(0xA7);//set normal/inverse display: A6H:normal/A7H:inverse
    // OLED_Display_On(); // power on
#endif
}
